package edu.uta.cfl.engine;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner;
import java.util.regex.Pattern;

import edu.uta.cfl.data.Parameter;
import edu.uta.cfl.data.SUT;
import edu.uta.cfl.data.SUT.SUTState;
import edu.uta.cfl.data.TestSet;
import edu.uta.cfl.util.Util;
import edu.uta.cfl.util.Util.MSGType;

public class SUTInfoReader {

	public static enum Section {
		undefined, system, group, parameter, relation, constraint, misc, testset, result
	};

	private Section currentSection;
	public final int LOAD = 0;
	public final int ADD = 1;

	File inputFile;
	boolean isXML;
	private SUT sut;

	public SUTInfoReader(String file) {
		if (file.endsWith(".xml"))
			isXML = true;
		else
			isXML = false;

		sut = new SUT();
		currentSection = Section.undefined;

		inputFile = new File(file);
		if (!inputFile.exists()) {
			System.out.println("File " + file + " does not exist!");
		}
	}

	private TestSet readTestSet(String filePath, ArrayList<Parameter> prms) {
		TestSet ts = new TestSet();

		FileReader fileReader = null;
		try {
			fileReader = new FileReader(filePath);
		}

		catch (Exception ex) {
			ex.printStackTrace();
		}

		Scanner scanner = new Scanner(fileReader);

		while (scanner.hasNext()) {
			String rec = scanner.nextLine();
			String[] att = rec.split("\t");
			int[] value = new int[prms.size()];
			for (int i = 0; i < prms.size(); i++)
				value[i] = prms.get(i).getValues().indexOf(att[i]);

			ts.getMatrix().add(value);
			ts.getResult().add(Integer.parseInt(att[prms.size()]));

		}
		return ts;
	}

	public SUT getSUT() {
		if (isXML)
			return parseXML();
		else
			return parseCSV();

	}

	public SUT parseCSV() {

		BufferedReader reader;
		ArrayList<int[]> addTC = new ArrayList<int[]>();
		try {
			reader = new BufferedReader(new FileReader(inputFile));
			String line;

			while ((line = reader.readLine()) != null) {
				if (Pattern.matches("\\[.*(?i)System.*\\]", line)) {
					currentSection = Section.system;
				} else if (Pattern.matches("Parameters\\p{Space}*:\\p{Space}*",
						line)) {
					currentSection = Section.parameter;
				} else if (Pattern.matches("Relations\\p{Space}*:\\p{Space}*",
						line)) {
					currentSection = Section.relation;
				} else if (Pattern.matches(
						"Constraints\\p{Space}*:\\p{Space}*", line)) {
					currentSection = Section.constraint;
				} else if (Pattern.matches("Tests\\p{Space}*", line)) {
					currentSection = Section.testset;
					sut.setStatus(SUTState.failureInducing);
					reader.readLine();
				} else if (Pattern.matches("Results\\p{Space}*", line)) {
					currentSection = Section.result;
					reader.readLine();
				} else if (Pattern.matches("", line.trim())) {
					currentSection = Section.undefined;
				} else {
					switch (currentSection) {
					case system:
						readSystemSection(line);
						break;
					case parameter:
						readParameterSection(line);
						break;
					case relation:
						readRelationSection(line);
						break;
					case constraint:
						readConstraintSection(line);
						break;
					case testset:
						addTC.add(readExistingTestSet(line, 0, LOAD));
						break;
					case result:
						readResultSection(line, addTC);
					default:
					}
				}
			}
		} catch (IOException ex) {
			Util.showErr("Errors encountered when reading input file!");
		}

		ArrayList<Integer> delLst = new ArrayList<Integer>();
		for (int[] tc : addTC)
			if (TestCaseManager.frequency(tc, sut.getTs().getMatrix()) > 1)
				for (int i = TestCaseManager.firstOccurrence(tc, sut.getTs()
						.getMatrix()) + 1; i < sut.getTs().getMatrix().size(); i++)
					if (TestCaseManager.equals(sut.getTs().getMatrix().get(i),
							tc))
						delLst.add(i);

		for (int r : sut.getTs().getResult())
			if (r == sut.getTs().UNDEFINED)
				delLst.add(sut.getTs().getResult().indexOf(r));

		ArrayList<Integer> tmpLst = new ArrayList<Integer>();
		for (int i : delLst) {
			if (!tmpLst.contains(i))
				tmpLst.add(i);
		}

		delLst.clear();
		delLst.addAll(tmpLst);
		Collections.sort(delLst);
		Collections.reverse(delLst);

		for (int i : delLst) {
			sut.getTs().getMatrix().remove(i);
			sut.getTs().getLevelAdded().remove(i);
			sut.getTs().getResult().remove(i);
		}

		sut.initiateArrayLsts();
		return this.sut;
	}

	private void readSystemSection(String line) {

	}

	private void readConstraintSection(String line) {
		this.sut.addConstraint(line);
	}

	private void readParameterSection(String line) {
		line = line.replace("\"", "");
		Pattern paramPattern = Pattern.compile(":");
		String[] tokens = paramPattern.split(line);

		tokens[1] = tokens[1].replace("[", "");
		tokens[1] = tokens[1].replace("]", "");

		Pattern valuePattern = Pattern.compile(",");
		String[] valTokens = valuePattern.split(tokens[1]);

		ArrayList<String> vals = new ArrayList<String>();
		Parameter prm = new Parameter(tokens[0], vals);
		for (int i = 0; i < valTokens.length; i++)
			prm.getValues().add(valTokens[i].trim());

		this.sut.addParameter(prm);
	}

	private void readRelationSection(String line) {
		line = line.replace("\"", "");
		Pattern paramPattern = Pattern.compile(",");
		String[] tokens = paramPattern.split(line);

		tokens[0] = tokens[0].replace("[", "");

		this.sut.setStrength(Integer.parseInt(tokens[0]));
	}

	private int[] readExistingTestSet(String line, int level, int type) {
		line = line.replace("\"", "");
		Pattern paramPattern = Pattern.compile(",");
		String[] tokens = paramPattern.split(line);

		int[] tc = new int[sut.getParameters().size()];
		Arrays.fill(tc, -1);

		int initIdx;
		if (type == ADD)
			initIdx = 1;
		else
			initIdx = 0;

		try {
			for (int i = 0; i < tc.length; i++) {
				tc[i] = sut.getParameters().get(i).getValues()
						.indexOf(tokens[i + initIdx].trim());
				if (tc[i] == -1)
					Util.showErr("Test case has an invalid value.\n"
							+ tokens[i + initIdx].trim()
							+ " is not a valid value for parameter "
							+ sut.getParameters().get(i).getName());
			}

			this.sut.getTs().getMatrix().add(tc);
			this.sut.getTs().getLevelAdded().add(level);
			this.sut.getTs().getResult().add(sut.getTs().UNDEFINED);

		} catch (Exception e) {
			Util.showErr("Test case has an invalid value.");
		}
		return tc;
	}

	private void readResultSection(String line, ArrayList<int[]> tcList) {
		line = line.replace("\"", "");
		Pattern paramPattern = Pattern.compile(",");
		String[] tokens = paramPattern.split(line);
		boolean passExist = false;
		try {
			int[] tc = tcList.get(Integer.parseInt(tokens[0].trim()));
			int idx = sut.getTs().getMatrix().indexOf(tc);
			if (idx != -1) {
				if (Pattern.matches("fail", tokens[1].trim()))
					this.sut.getTs().getResult().set(idx, sut.getTs().FAIL);
				else if (Pattern.matches("pass", tokens[1].trim())) {
					this.sut.getTs().getResult().set(idx, sut.getTs().PASS);
					passExist = true;
				} else
					Util.showMsg("Unknown status in result section for test# = "
							+ tokens[0].trim() + ".");
			} else
				Util.showErr(Util.getMsg(MSGType.inputParsing));
		} catch (IndexOutOfBoundsException e) {
			Util.showErr(Util.getMsg(MSGType.inputParsing));
		}
		if (passExist)
			sut.setStatus(SUTState.continuing);

	}

	public SUT parseXML() {
		SUT sut = new SUT();
		/*
		 * SAXBuilder builder = new SAXBuilder();
		 *
		 *
		 * try{ Document doc = (Document) builder.build(inputFile); Element root
		 * = doc.getRootElement(); sut = buildData(root); } catch (Exception e)
		 * { Util.showErr("An error occured during parsing."); }
		 */return sut;
	}

	/*
	 * private SUT buildData(Element root) { Attribute attr =
	 * root.getAttribute("name");
	 *
	 * SUT sut = new SUT();
	 *
	 *
	 * String sysName = attr.getValue(); sut.setName(sysName);
	 *
	 * Element params = root.getChild("Parameters");// Parameter Container
	 *
	 * List paraList = params.getChildren(); ArrayList<Parameter>
	 * parameterOrderList = new ArrayList<Parameter>();
	 *
	 * for (Iterator it = paraList.iterator(); it.hasNext();) { // each
	 * parameter ArrayList<String> paramValue = new ArrayList<String>();
	 *
	 * Element parameter = (Element) it.next(); Element values =
	 * parameter.getChild("values");
	 *
	 * List valueList = values.getChildren("value"); for (Iterator valIter =
	 * valueList.iterator(); valIter.hasNext();) { Element value = (Element)
	 * valIter.next(); paramValue.add(value.getText()); }
	 *
	 * String paramName = parameter.getAttributeValue("name"); String paramType
	 * = parameter.getAttributeValue("type"); String paramID =
	 * parameter.getAttributeValue("id");
	 *
	 * Parameter newParam = new Parameter(paramName,paramValue);
	 *
	 *
	 * parameterOrderList.add(newParam); }
	 *
	 * sut.setParameters(parameterOrderList);
	 *
	 * Element testSet = root.getChild("Testset");// Testset Container List
	 * tcList = testSet.getChildren(); ArrayList<int[]> tsM = new
	 * ArrayList<int[]>(); for (Iterator it = tcList.iterator(); it.hasNext();)
	 * { // each testcase int[] tcValue = new int[parameterOrderList.size()];
	 * Element tc = (Element) it.next();
	 *
	 * List valueList = tc.getChildren(); int idx=0; Iterator valIter =
	 * valueList.iterator(); Element value = (Element) valIter.next(); while (
	 * valIter.hasNext()) { value = (Element) valIter.next(); int v =
	 * parameterOrderList.get(idx).getValues().indexOf(value.getText());
	 * tcValue[idx++] = v; } tsM.add(tcValue); }
	 *
	 * Element relations = root.getChild("Relations");// Relation Container List
	 * childs = relations.getChildren(); Iterator theIter = childs.iterator();
	 * Element relation = (Element) theIter.next(); String stren =
	 * relation.getAttributeValue("Strength");
	 *
	 *
	 *
	 *
	 * sut.setStrength(Integer.parseInt(stren)); TestSet ts = new TestSet();
	 * ts.setMatrix(tsM);
	 *
	 * sut.setTs(ts); return sut;
	 *
	 * }
	 */

	public void addTestSet(SUT sut) {
		this.sut = sut;
		int level = findNextLevel();
		ArrayList<int[]> addTC = new ArrayList<int[]>();
		BufferedReader reader;
		try {
			reader = new BufferedReader(new FileReader(inputFile));
			String line;
			int call = 0; //to be removed
			while ((line = reader.readLine()) != null) {
				if (Pattern.matches("Tests\\p{Space}*", line)) {

					currentSection = Section.testset;
					reader.readLine();
				} else if (Pattern.matches("Results\\p{Space}*", line)) {
					currentSection = Section.result;
					reader.readLine();
				} else if (Pattern.matches("", line.trim())) {
					currentSection = Section.undefined;
				} else {
					switch (currentSection) {
					case testset:
						addTC.add(readExistingTestSet(line, level, ADD));
						break;
					case result:
						readResultSection(line, addTC);
					default:
					}
				}
			}
		} catch (IOException ex) {
			BufferedReader stdin = new BufferedReader(new InputStreamReader(
					System.in));
			System.out.println("Do you want to change the input file(y/n):");
			String ans;
			String file;
			try {
				while ((ans = stdin.readLine()).length() == 0)
					;
				if (ans.equalsIgnoreCase("n"))
					Util.showErr("Errors encountered when reading input file!");
				else {
					System.out.println("Enter the new file name:");
					while ((file = stdin.readLine()).length() == 0)
						;
					inputFile = new File(file);
					if (!inputFile.exists())
						System.out.println("File " + file + " does not exist!");
					else
						addTestSet(sut);
				}

			} catch (IOException e) {
				// TODO Auto-generated catch block
				Util.showErr("Errors encountered when reading input file!");
			}

		}

		ArrayList<Integer> delLst = new ArrayList<Integer>();
		for (int[] tc : addTC)
			if (TestCaseManager.frequency(tc, sut.getTs().getMatrix()) > 1)
				for (int i = TestCaseManager.firstOccurrence(tc, sut.getTs()
						.getMatrix()) + 1; i < sut.getTs().getMatrix().size(); i++)
					if (TestCaseManager.equals(sut.getTs().getMatrix().get(i),
							tc))
						delLst.add(i);

		for (int i = 0; i < sut.getTs().getMatrix().size(); i++)
			if (sut.getTs().getResult().get(i) == sut.getTs().UNDEFINED)
				delLst.add(i);

		ArrayList<Integer> tmpLst = new ArrayList<Integer>();
		for (int i : delLst) {
			if (!tmpLst.contains(i))
				tmpLst.add(i);
		}

		delLst.clear();
		delLst.addAll(tmpLst);
		Collections.sort(delLst);
		Collections.reverse(delLst);

		for (int i : delLst) {
			sut.getTs().getMatrix().remove(i);
			sut.getTs().getLevelAdded().remove(i);
			sut.getTs().getResult().remove(i);
		}

	}

	private int findNextLevel() {
		return Collections.max(sut.getTs().getLevelAdded()) + 1;

	}

}